`timescale 1ns / 1ps

`include "data_width.vh"

module get_dst_id #(parameter
    DST_ID_DWIDTH = `DST_ID_DWIDTH,
    VERTEX_PIPE_NUM = `VERTEX_PIPE_NUM,
    EDGE_OFF_DWIDTH = `EDGE_OFF_DWIDTH,
    MEM_AWIDTH = `MEM_AWIDTH,
    VERTEX_MASK_WIDTH = `VERTEX_MASK_WIDTH
    ) (
        input                                                   clk,
        input                                                   front_rst,
        input                                                   back_stage_vertex_full,

        // para in
        input [DST_ID_DWIDTH - 1 : 0]                           front_dst_id_ed,
        input [DST_ID_DWIDTH - 1 : 0]                           front_vertex_num,
        input [EDGE_OFF_DWIDTH - 1 : 0]                         front_edge_off_ed,
        input [MEM_AWIDTH - 1 : 0]                              front_edge_off_addr_ed,
        input [MEM_AWIDTH - 1 : 0]                              front_edge_info_addr_ed,
        input [EDGE_OFF_DWIDTH - VERTEX_MASK_WIDTH - 1 : 0]     front_mem_edge_ed,
        input                                                   front_para_valid,

        // para out
        output                                                  rst,
        output [DST_ID_DWIDTH - 1 : 0]                          dst_id_ed,
        output [EDGE_OFF_DWIDTH - 1 : 0]                        edge_off_ed,
        output [MEM_AWIDTH - 1 : 0]                             edge_off_addr_ed,
        output [MEM_AWIDTH - 1 : 0]                             edge_info_addr_ed,
        output [EDGE_OFF_DWIDTH - VERTEX_MASK_WIDTH - 1 : 0]    mem_edge_ed,
        output                                                  para_valid,

        // 点流水线信号组合成一个传递
        output [DST_ID_DWIDTH * VERTEX_PIPE_NUM - 1 : 0]        dst_id,
        output [VERTEX_PIPE_NUM - 1 : 0]                        dst_data_valid);

    wire read_next_valid;
    wire [VERTEX_PIPE_NUM - 1 : 0] dst_buffer_empty, dst_buffer_full;

    get_dst_id_para_trans P (
        .clk(clk),
        .front_rst(front_rst),
        .front_dst_id_ed(front_dst_id_ed),
        .front_edge_off_ed(front_edge_off_ed),
        .front_edge_off_addr_ed(front_edge_off_addr_ed),
        .front_edge_info_addr_ed(front_edge_info_addr_ed),
        .front_mem_edge_ed(front_mem_edge_ed),
        .front_para_valid(front_para_valid),

        .rst(rst),
        .dst_id_ed(dst_id_ed),
        .edge_off_ed(edge_off_ed),
        .edge_off_addr_ed(edge_off_addr_ed),
        .edge_info_addr_ed(edge_info_addr_ed),
        .mem_edge_ed(mem_edge_ed),
        .para_valid(para_valid));

    generate
        genvar i;
        for (i = 0; i < VERTEX_PIPE_NUM; i = i + 1) begin : M1_BLOCK_1
            get_dst_id_dp_single #(.LOC(i)) V (
                .clk(clk), .rst(front_rst),
                .back_stage_vertex_full(back_stage_vertex_full),
                .read_next_valid(read_next_valid),

                // parameter
                .front_vertex_num(front_vertex_num),
                .front_dst_id_ed(front_dst_id_ed), .front_para_valid(front_para_valid),

                .buffer_empty(dst_buffer_empty[i]),
                .buffer_full(dst_buffer_full[i]),
                .dst_id(dst_id[(i + 1) * DST_ID_DWIDTH - 1 : i * DST_ID_DWIDTH]),
                .dst_data_valid(dst_data_valid[i]));
        end
    endgenerate

    get_dst_id_control gdc (
        .buffer_full_all(dst_buffer_full),

        .read_next_valid(read_next_valid));

endmodule

module get_dst_id_para_trans #(parameter
    EDGE_OFF_DWIDTH     = `EDGE_OFF_DWIDTH,
    MEM_AWIDTH          = `MEM_AWIDTH,
    VERTEX_MASK_WIDTH   = `VERTEX_MASK_WIDTH,
    DST_ID_DWIDTH       = `DST_ID_DWIDTH
    ) (
        input clk,
        input front_rst,
        input [DST_ID_DWIDTH - 1 : 0]   front_dst_id_ed,
        input [EDGE_OFF_DWIDTH - 1 : 0] front_edge_off_ed,
        input [MEM_AWIDTH - 1 : 0] front_edge_off_addr_ed,
        input [MEM_AWIDTH - 1 : 0] front_edge_info_addr_ed,
        input [EDGE_OFF_DWIDTH - VERTEX_MASK_WIDTH - 1 : 0] front_mem_edge_ed,
        input front_para_valid,

        output reg rst,
        output reg [DST_ID_DWIDTH - 1 : 0]   dst_id_ed,
        output reg [EDGE_OFF_DWIDTH - 1 : 0] edge_off_ed,
        output reg [MEM_AWIDTH - 1 : 0] edge_off_addr_ed,
        output reg [MEM_AWIDTH - 1 : 0] edge_info_addr_ed,
        output reg [EDGE_OFF_DWIDTH - VERTEX_MASK_WIDTH - 1 : 0] mem_edge_ed,
        output reg para_valid);

    // rst
    always @ (posedge clk) begin
        rst <= front_rst;
    end

    // parameter
    always @ (posedge clk) begin
        if (front_rst) begin
            dst_id_ed           <= 0;
            edge_off_ed         <= 0;
            edge_off_addr_ed    <= 0;
            edge_info_addr_ed   <= 0;
            mem_edge_ed         <= 0;
            para_valid          <= 1'b0;
        end
        else begin
            if (front_para_valid) begin
                dst_id_ed           <= front_dst_id_ed;
                edge_off_ed         <= front_edge_off_ed;
                edge_off_addr_ed    <= front_edge_off_addr_ed;
                edge_info_addr_ed   <= front_edge_info_addr_ed;
                mem_edge_ed         <= front_mem_edge_ed;
                para_valid          <= 1'b1;
            end
        end
    end

endmodule

module get_dst_id_dp_single #(parameter
    LOC = 0,
    DST_ID_DWIDTH = `DST_ID_DWIDTH,
    DST_ID_ST = `DST_ID_ST,
    VERTEX_PIPE_NUM = `VERTEX_PIPE_NUM
    ) (
        input clk,
        input rst,
        input read_next_valid,
        input back_stage_vertex_full,

        input [DST_ID_DWIDTH - 1 : 0] front_dst_id_ed,
        input [DST_ID_DWIDTH - 1 : 0] front_vertex_num,
        input front_para_valid,

        output buffer_empty,
        output buffer_full,
        output [DST_ID_DWIDTH - 1 : 0] dst_id,
        output dst_data_valid);

    reg [DST_ID_DWIDTH - 1 : 0] now_dst_id;
    wire tmp_dst_data_valid;
    wire buffer_full_tmp, wr_rst_busy, rd_rst_busy;

    always @ (posedge clk) begin
        if (rst) begin
            now_dst_id <= DST_ID_ST + LOC;
        end
        else begin
            if (read_next_valid && !wr_rst_busy && front_para_valid) begin
                if (now_dst_id == front_dst_id_ed + LOC) begin
                    now_dst_id <= DST_ID_ST + LOC;
                end
                else begin
                    now_dst_id <= now_dst_id + VERTEX_PIPE_NUM;
                end
            end
        end
    end

    // dst_id 有效还需满足 dst_id < VERTEX_NUM
    assign dst_data_valid = (tmp_dst_data_valid && (dst_id < front_vertex_num));

    dst_id_fifo DI1 (
        .clk(clk), .srst(rst),
        .din(now_dst_id), 
        .wr_en(read_next_valid && !wr_rst_busy && front_para_valid), .rd_en(!(back_stage_vertex_full)),

        .dout(dst_id), .valid(tmp_dst_data_valid),
        .empty(buffer_empty), .prog_full(buffer_full),
        .full(buffer_full_tmp),
        .wr_rst_busy(wr_rst_busy), .rd_rst_busy(rd_rst_busy));

endmodule

module get_dst_id_control #(parameter
    VERTEX_PIPE_NUM = `VERTEX_PIPE_NUM
    ) (
    input [VERTEX_PIPE_NUM - 1 : 0] buffer_full_all,

    output read_next_valid);

    assign read_next_valid = !(|buffer_full_all);

endmodule